(function () {
    "use strict";

    require("./slice-args");
    var defineType = require("./define-type");

    var dotRx = /^([^\\.]|\\.)*[.]/m;
    var escapeEscapeRx = /\\/gm;
    var unescapeEscapeRx = /\\\\/gm;
    var escapeDotRx = /\./gm;
    var unescapeDotRx = /\\./gm;

    function splitFirst(path) {
        if (!path) {
            return null;
        }

        var m = path.match(dotRx);
        if (!m) {
            return null;
        }

        var length = m[0].length;
        return {
            first: path.substr(0, length - 1)
                .replace(unescapeEscapeRx, "\\")
                .replace(unescapeDotRx, "."),
            rest: path.substr(length)
        };
    }

    function escapeName(name) {
        return String(name)
            .replace(escapeEscapeRx, "\\\\")
            .replace(escapeDotRx, "\\.");
    }

    function join() {
        if (!arguments.length) {
            return undefined;
        }

        var current = arguments[0];
        if (current === null || current === undefined) {
            current = "";
        }
        current = escapeName(current);

        if (arguments.length === 1) {
            return current;
        }

        var rest = PathWalker.join.apply(this, Function.sliceArgs(arguments, 1));
        if (!current) {
            return rest;
        }

        if (!rest) {
            return current;
        }

        return current + "." + rest;
    }

    function get(instance, path) {
        var parsed = splitFirst(path);
        if (!parsed) {
            return instance[path];
        }

        return PathWalker.get(instance[parsed.first], parsed.rest);
    }

    function set(instance, path, value) {
        var parsed = splitFirst(path);
        if (!parsed) {
            instance[path] = value;
            return value;
        }

        return PathWalker.set(instance[parsed.first], parsed.rest, value);
    }

    var PathWalker = defineType({
        get: function (path) {
            return get(this, path);
        },

        set: function (path, value) {
            return set(this, path, value);
        }
    });

    PathWalker.get = get;
    PathWalker.set = set;
    PathWalker.escapeName = escapeName;
    PathWalker.splitFirst = splitFirst;
    PathWalker.join = join;

    module.exports = PathWalker;
})();
